<template>
  <el-row :gutter="20">
    <el-col :span="4">
      <ContentWrap>
        <avue-tree
          ref="treeRef"
          :option="treeOption"
          :data="treeData"
          @node-click="nodeClick"
        ></avue-tree>
      </ContentWrap>
    </el-col>
    <el-col :span="20">
      <ContentWrap>
        <avue-crud
          ref="crudRef"
          v-model="tableForm"
          v-model:page="tablePage"
          v-model:search="tableSearch"
          :table-loading="loading"
          :data="tableData"
          :option="tableOption"
          :permission="permission"
          :before-open="beforeOpen"
          @search-change="searchChange"
          @search-reset="resetChange"
          @row-save="rowSave"
          @row-update="rowUpdate"
          @refresh-change="getTableData"
          @size-change="sizeChange"
          @current-change="currentChange"
        >
          <template #menu-left="{ size }">
            <el-button
              type="warning"
              plain
              @click="handleImport"
              v-hasPermi="['system:user:import']"
            >
              <Icon icon="ep:upload" /> 导入
            </el-button>
            <el-button
              type="success"
              plain
              :size="size"
              icon="el-icon-download"
              @click="handleExport"
              :loading="exportLoading"
              v-hasPermi="['system:user:export']"
              >导出</el-button
            >
          </template>
          <template #menu="{ row }">
            <el-dropdown
              @command="(command) => handleCommand(command, row)"
              v-hasPermi="[
                'system:user:delete',
                'system:user:update-password',
                'system:permission:assign-user-role'
              ]"
            >
              <div class="pt-3px pr-2px pb-4px pl-2px cursor-pointer">
                <el-text type="primary">
                  <span>更多</span>
                  <Icon :size="16" icon="iconamoon:arrow-down-2-light" />
                </el-text>
              </div>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item
                    command="handleResetPwd"
                    v-if="checkPermi(['system:user:update-password'])"
                  >
                    <Icon icon="ep:key" />重置密码
                  </el-dropdown-item>
                  <el-dropdown-item
                    command="handleDelete"
                    v-if="checkPermi(['system:user:delete'])"
                  >
                    <Icon icon="ep:delete" />删除
                  </el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </template>
          <template #status="scope">
            <el-switch
              v-if="scope.row.status != undefined"
              v-model="scope.row.status"
              :active-value="0"
              :inactive-value="1"
              @change="handleStatusChange(scope.row)"
            />
          </template>
          <template #deptName="{ row }">
            <div class="flex flex-wrap gap-2px justify-center">
              <div v-for="(dept, deptIndex) in row.deptInfoList" :key="dept.deptId">
                <el-popover placement="right" width="240" trigger="hover">
                  <template #reference>
                    <el-tag type="primary" class="cursor-pointer">{{ dept.deptName }}</el-tag>
                  </template>
                  <div class="flex flex-col gap-y-4px">
                    <template v-for="type in keyList" :key="type.value">
                      <div class="flex" v-if="row.deptInfoList[deptIndex][type.value]">
                        <span class="flex-basis-42px flex-shrink-0 c-#909399"
                          >{{ type.label }}：</span
                        >
                        <span class="flex-1">{{ row.deptInfoList[deptIndex][type.value] }}</span>
                      </div>
                    </template>
                  </div>
                </el-popover>
              </div>
            </div>
          </template>
          <template #deptInfoList-form>
            <avue-crud ref="deptCrudRef" :option="deptOption" :data="tableForm.deptInfoList">
              <template #menu="{ size, index }">
                <el-button text :size="size" type="danger" @click="delDeptTableRow(index)">
                  <Icon icon="ep:delete"></Icon>
                  <span>删除</span>
                </el-button>
              </template>
              <template #deptId-form="{ index, column, disabled }">
                <DeptSelect
                  v-model="tableForm.deptInfoList[index].deptId"
                  :column="column"
                  :disabled="disabled"
                  type="edit"
                  prop="deptId"
                ></DeptSelect>
              </template>
            </avue-crud>
          </template>
        </avue-crud>
      </ContentWrap>
    </el-col>
  </el-row>
  <!-- 用户导入对话框 -->
  <UserImportForm ref="importFormRef" @success="getTableData" />
</template>

<script setup lang="ts">
import * as UserApi from '@/api/system/user'
import * as DeptApi from '@/api/system/dept'
import { checkPermi } from '@/utils/permission'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { handleTree } from '@/utils/tree'
import { CommonStatusEnum } from '@/utils/constants'
import { dateFormatter, getSearchDate } from '@/utils/formatTime'
import download from '@/utils/download'
import { useLowStoreWithOut } from '@/store/modules/low'
import UserImportForm from './UserImportForm.vue'
import { cloneDeep } from 'lodash-es'

defineOptions({ name: 'SystemUser' })

const keyList = [
  { label: '部门', value: 'deptName' },
  { label: '角色', value: 'roleInfoList' },
  { label: '职务', value: 'dutyInfoList' },
  { label: '职位', value: 'positionInfoList' },
  { label: '岗位', value: 'postInfoList' }
]
// 树数据
const treeOption = reactive({
  defaultExpandAll: true,
  addBtn: false,
  formOption: {
    column: [{ label: '自定义项', prop: 'test' }]
  },
  props: { label: 'name', value: 'id', children: 'children' }
})
const treeData = ref<Tree[]>([])
const crudRef = ref()
const treeRef = ref()
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const { getCurrPermi } = useCrudPermi()
const lowStore = useLowStoreWithOut()

const loading = ref(true) // 列表的加载中
const tableOption = ref({
  delBtn: false,
  align: 'center',
  headerAlign: 'center',
  searchMenuSpan: 6,
  searchMenuPosition: 'left',
  labelSuffix: ' ',
  span: 12,
  dialogWidth: '80%',
  menuWidth: 160,
  dialogCustomClass: 'system-user-form',
  column: {
    username: { label: '用户账号', search: true, minWidth: 90, display: false },
    nickname: { label: '用户昵称', search: true, minWidth: 100, display: false },
    deptName: { label: '部门', minWidth: 100, display: false },
    mobile: { label: '手机号码', search: true, width: 120, display: false },
    status: {
      label: '状态',
      type: 'select',
      dicData: getIntDictOptions(DICT_TYPE.COMMON_STATUS),
      search: true,
      display: false
    },
    createTime: {
      label: '创建时间',
      search: true,
      searchRange: true,
      type: 'date',
      searchType: 'daterange',
      valueFormat: 'YYYY-MM-DD',
      width: 180,
      startPlaceholder: '开始时间',
      endPlaceholder: '结束时间',
      formatter: (row, val, value, column) => {
        return dateFormatter(row, column, val)
      },
      display: false
    }
  },
  group: [
    {
      label: '基本信息',
      collapse: true,
      prop: 'group_info',
      column: {
        username: {
          label: '用户账号',
          addDisplay: true,
          editDisplay: false,
          viewDisplay: false,
          rules: [{ required: true, message: '用户账号不能为空', trigger: 'blur' }]
        },
        password: {
          label: '用户密码',
          hide: true,
          addDisplay: true,
          editDisplay: false,
          viewDisplay: false,
          rules: [{ required: true, message: '用户密码不能为空', trigger: 'blur' }]
        },
        nickname: {
          label: '用户昵称',
          rules: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }]
        },
        mobile: { label: '手机号码' },
        email: {
          label: '邮箱',
          rules: [
            {
              validator: (rule, value, callback) => {
                if (
                  value === '' ||
                  /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value)
                ) {
                  callback()
                } else callback(new Error('请输入正确的邮箱'))
              },
              trigger: 'blur'
            }
          ]
        },
        sex: {
          label: '用户性别',
          type: 'select',
          dicData: getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX),
          value: ''
        },
        rankIds: {
          label: '职级',
          type: 'select',
          multiple: true,
          cell: true,
          dicUrl: '/system/rank/simple-list',
          dataType: 'array',
          props: { label: 'name', value: 'id' }
        },
        remark: { label: '备注', span: 24, type: 'textarea', minRows: 2, maxRows: 4 }
      }
    },
    {
      label: '部门信息',
      collapse: true,
      prop: 'group_dept',
      labelWidth: 20,
      display: true,
      column: {
        deptInfoList: {
          hide: false,
          span: 24,
          dataType: 'array',
          value: [
            { $cellEdit: true, deptId: '', roleIds: [], dutyIds: [], positionIds: [], postIds: [] }
          ],
          rules: [
            {
              validator: (rule, value, callback) => {
                if (value?.length) callback()
                else callback(new Error('请添加用户部门信息'))
              },
              trigger: 'blur'
            }
          ]
        }
      }
    }
  ]
}) //表格配置
const tableForm = ref<any>({})
const tableData = ref([])
const tableSearch = ref<any>({})
const tablePage = ref({
  currentPage: 1,
  pageSize: 10,
  total: 0,
  deptId: null
})
const permission = getCurrPermi(['system:user'])
const exportLoading = ref(false) // 导出的加载中

const deptOption = ref({
  addBtn: false,
  editBtn: false,
  addRowBtn: true,
  refreshBtn: false,
  columnBtn: false,
  addBtnText: '新增部门',
  index: true,
  menuWidth: 120,
  height: undefined,
  calcHeight: undefined,
  maxHeight: 300,
  column: {
    deptId: {
      label: '所属部门',
      findType: 'all',
      multiple: false,
      cell: true,
      rules: [{ required: true, message: '请选择所属部门', trigger: 'change' }]
    },
    roleIds: {
      label: '角色',
      type: 'select',
      multiple: true,
      cell: true,
      dataType: 'array',
      dicUrl: '/system/role/simple-list',
      props: { label: 'name', value: 'id' },
      dicFormatter: (res) => {
        //剔除超级管理员和租户管理员
        return res.filter((item) => !['super_admin', 'tenant_admin'].includes(item.code))
      }
    },
    dutyIds: {
      label: '职务',
      type: 'select',
      multiple: true,
      cell: true,
      dataType: 'array',
      dicUrl: '/system/duty/simple-list',
      props: { label: 'name', value: 'id' }
    },
    positionIds: {
      label: '职位',
      type: 'select',
      multiple: true,
      cell: true,
      dataType: 'array',
      dicUrl: '/system/position/simple-list',
      props: { label: 'name', value: 'id' }
    },
    postIds: {
      label: '岗位',
      type: 'select',
      multiple: true,
      cell: true,
      dataType: 'array',
      dicUrl: '/system/post/simple-list',
      props: { label: 'name', value: 'id' }
    }
  }
})

const deptCrudRef = ref()

const nodeClick = (row: any) => {
  if (tablePage.value.deptId == row.id) {
    tablePage.value.deptId = null
    treeRef.value.setCurrentKey(null)
  } else {
    tablePage.value.deptId = row.id
  }
  getTableData()
}

/** 查询列表 */
const getTableData = async () => {
  loading.value = true
  let searchObj = {
    ...tableSearch.value,
    pageNo: tablePage.value.currentPage,
    pageSize: tablePage.value.pageSize,
    deptId: tablePage.value.deptId
  }

  if (searchObj.createTime?.length) {
    searchObj.createTime = getSearchDate(searchObj.createTime)
  } else delete searchObj.createTime

  for (let key in searchObj) if (searchObj[key] === '') delete searchObj[key]
  try {
    const data = await UserApi.getUserPage(searchObj)
    tableData.value = data.list.map((item) => {
      item.deptInfoList = item.deptInfoList.map((dept) => {
        keyList.forEach(({ value }) => {
          if (value === 'deptName') return
          if (dept[value]?.length) dept[value] = dept[value].map((info) => info.name).join('、')
          else dept[value] = ''
        })
        return dept
      })
      return item
    })
    tablePage.value.total = data.total
  } finally {
    loading.value = false
  }
}

useCrudHeight(crudRef)

/** 搜索按钮操作 */
const searchChange = (params, done) => {
  tablePage.value.currentPage = 1
  getTableData().finally(() => {
    done()
  })
}

/** 清空按钮操作 */
const resetChange = () => {
  searchChange({}, () => {})
}

const sizeChange = (pageSize) => {
  tablePage.value.pageSize = pageSize
  resetChange()
}

const currentChange = (currentPage) => {
  tablePage.value.currentPage = currentPage
  getTableData()
}

/** 表单打开前 */
const beforeOpen = async (done, type) => {
  let bool = true
  if (['edit', 'view'].includes(type) && tableForm.value.id) {
    loading.value = true
    const currData = await UserApi.getUser(tableForm.value.id)
    if (currData.rankInfoList?.length) {
      currData.rankIds = currData.rankInfoList.map((item) => item.id)
    }
    delete currData.rankInfoList
    if (currData.deptInfoList?.length) {
      const deptObj = {}
      currData.deptInfoList = currData.deptInfoList.map((dept) => {
        keyList.forEach(({ value }) => {
          dept.deptId = dept.deptId + ''
          if (value === 'deptName') deptObj[dept.deptId] = dept[value]
          else {
            const key = value.split('InfoList')[0] + 'Ids'
            dept[key] = dept[value].map((info) => info.id)
          }
          delete dept[value]
        })
        if (type == 'edit') dept.$cellEdit = true
        return dept
      })
      lowStore.setDicObj('deptSelect', deptObj)
    } else {
      currData.deptInfoList = cloneDeep(tableOption.value.group[1].column.deptInfoList?.value)
    }
    if (currData.supAdminFlag || currData.tenantAdminFlag) {
      delete currData.deptInfoList
      bool = false
    }
    tableForm.value = currData
    loading.value = false
  }
  tableOption.value.group[1].display = bool
  done()
}

const verifydeptTable = () => {
  return new Promise((resolve) => {
    if (!deptCrudRef.value) return resolve(true)
    deptCrudRef.value.validateCellForm().then((res) => {
      if (res) {
        const dom = document.querySelector('.system-user-form .el-dialog__body')
        if (dom) dom.scrollTop = dom.scrollHeight
      }
      resolve(!res)
    })
  })
}

/** 新增操作 */
const rowSave = async (form, done, loading) => {
  const verify = await verifydeptTable()
  if (!verify) return loading()
  let bool = await UserApi.createUser(form).catch(() => false)
  if (bool) {
    message.success(t('common.createSuccess'))
    resetChange()
    done()
  } else loading()
}

/** 编辑操作 */
const rowUpdate = async (form, index, done, loading) => {
  const verify = await verifydeptTable()
  if (!verify) return loading()
  delete form.password
  if (form.supAdminFlag || form.tenantAdminFlag) delete form.deptInfoList
  let bool = await UserApi.updateUser(form).catch(() => false)
  if (bool) {
    message.success(t('common.updateSuccess'))
    getTableData()
    done()
  } else loading()
}

/** 操作分发 */
const handleCommand = (command: string, row: UserApi.UserVO) => {
  switch (command) {
    case 'handleDelete':
      rowDel(row)
      break
    case 'handleResetPwd':
      handleResetPwd(row)
      break
    case 'handleRole':
      handleRole(row)
      break
    default:
      break
  }
}

/** 删除按钮操作 */
const rowDel = async (form) => {
  try {
    // 删除的二次确认
    await message.delConfirm()
    // 发起删除
    await UserApi.deleteUser(form.id)
    message.success(t('common.delSuccess'))
    // 刷新列表
    await getTableData()
  } catch {}
}

/** 重置密码 */
const handleResetPwd = async (row: UserApi.UserVO) => {
  try {
    // 重置的二次确认
    const result = await message.prompt(
      '请输入"' + row.username + '"的新密码',
      t('common.reminder')
    )
    const password = result.value
    // 发起重置
    await UserApi.resetUserPwd(row.id, password)
    message.success('修改成功，新密码是：' + password)
  } catch {}
}

/** 分配角色 */
const assignRoleFormRef = ref()
const handleRole = (row: UserApi.UserVO) => {
  assignRoleFormRef.value.open(row)
}

/** 修改用户状态 */
const handleStatusChange = async (row: UserApi.UserVO) => {
  try {
    // 修改状态的二次确认
    const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用'
    await message.confirm('确认要"' + text + '""' + row.username + '"用户吗?')
    // 发起修改状态
    await UserApi.updateUserStatus(row.id, row.status)
    // 刷新列表
    await getTableData()
  } catch {
    // 取消后，进行恢复按钮
    row.status =
      row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE
  }
}

/** 用户导入 */
const importFormRef = ref()
const handleImport = () => {
  importFormRef.value.open()
}

/** 导出按钮操作 */
const handleExport = async () => {
  try {
    // 导出的二次确认
    await message.exportConfirm()
    // 发起导出
    exportLoading.value = true
    let searchObj = { ...tableSearch.value }
    if (tablePage.value.deptId) {
      searchObj.deptId = tablePage.value.deptId
    }
    for (let key in searchObj) if (searchObj[key] === '') delete searchObj[key]
    const data = await UserApi.exportUser(searchObj)
    download.excel(data, '用户管理列表.xls')
  } catch {
  } finally {
    exportLoading.value = false
  }
}

const delDeptTableRow = (index) => {
  tableForm.value.deptInfoList.splice(index, 1)
}

onMounted(async () => {
  DeptApi.getSimpleDeptList().then((res) => {
    const tree = handleTree(res)
    treeData.value = tree
  })
  getTableData()
})
</script>

<style lang="scss" scoped>
.el-select {
  width: 100%;
}

.el-dropdown {
  padding: 4px 2px;
}
</style>
<style lang="scss">
.system-user-form {
  .el-dialog__body {
    padding-top: 0 !important;
  }

  .avue-crud {
    .avue-crud__empty {
      padding: 0;

      .el-empty {
        padding: 0;
      }

      .el-empty__description {
        margin: 0;
      }
    }
  }
}
</style>
